﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.IO;
using System.Net.Json;

namespace OBNEPGGrabber
{
    interface IStatusUpdate
    {
        void UpdateStatus(string strUpdate, bool bFatal);
    }

    public partial class OBNEPGGrabberSvc : ServiceBase, IStatusUpdate
    {
        private static List<Channel> m_lstChannel = null;
        private static DirectoryInfo m_diChannel = null;
        private static DirectoryInfo m_diEPG = null;
        private static string m_strTVMenuURL = "http://w1.zt6.nl/tvmenu/";
        private static string m_strEPGURL = "http://w.zt6.nl/epgdata/";
        private static int m_iDaysToFetch = 10;
        private Thread m_thrdFetcher = null;

        delegate void UpdateStatusCallback(string strStatus, bool bFatal);
        
        public OBNEPGGrabberSvc()
        {
            InitializeComponent();

            //First register our own event log
            if (!System.Diagnostics.EventLog.SourceExists("OBNEPGGrabberSvc"))
            {
                System.Diagnostics.EventLog.CreateEventSource(
                    "OBNEPGGrabberSvc", "OBNEPGGrabberSvcLog");
            }
            // Configure the event log instance to use this source name
            eventLogOBNEPGGrabber.Source = "OBNEPGGrabberSvc";
        }

        public void UpdateStatus(string strUpdate, bool bFatal)
        {
            EventLogEntryType logEntryType;

            logEntryType = bFatal ? EventLogEntryType.Error : EventLogEntryType.Information; 

            // In this service, any status update will be put in the event log
            eventLogOBNEPGGrabber.WriteEntry(strUpdate,logEntryType);
        }

        // Main function with an endless loop to fetch data every 24 hours
        public void FetchData()
        {
            TimeSpan aDay = new TimeSpan(24,0,0);
            DateTime dt;

            try
            {
                // Create temp folders to hold channel and EPG data
                m_diChannel = CreateTempFolder("channel", this);
                m_diEPG = CreateTempFolder("epg", this);
            }
            catch (Exception)
            {
                eventLogOBNEPGGrabber.WriteEntry("Error creating temporary folders...bailing out.", EventLogEntryType.Error);
                return;
            }

            while (true)
            {
                // Prune cache, i.e. delete all data of days in the past
                PruneEPGCache(m_diEPG);

                // Fetch the index page holding the path to the file containing channel info
                FileFetcher.Fetch(m_strTVMenuURL + "index.xhtml.gz", m_diChannel, "index.xhtml.gz", (IStatusUpdate)this);
                Unzipper.Unzip("index.xhtml.gz", m_diChannel, "index.xhtml", true, (IStatusUpdate)this);
                string strCodeFile = CodeFinder.FindCode(m_diChannel, "index.xhtml", (IStatusUpdate)this);
                // Fetch gzip file containing channel info
                FileFetcher.Fetch(m_strTVMenuURL + strCodeFile, m_diChannel, "code.js.gz", (IStatusUpdate)this);
                Unzipper.Unzip("code.js.gz", m_diChannel, "code.js", true, (IStatusUpdate)this);
                // This local variable will hold all channels
                m_lstChannel = new List<Channel>();
                ChannelParser.Parse(m_diChannel, "code.js", ref m_lstChannel, (IStatusUpdate)this);
                // Create the file containing all channels (input for DVBLink)
                M3UGenerator.Generate(m_diChannel, ref m_lstChannel, (IStatusUpdate)this);
                
                // Now fetch all EPG data and store the file in the DVBLink folder
                EPGFetcher.FetchEPG(m_strEPGURL, m_diEPG, m_iDaysToFetch, ref m_lstChannel, (IStatusUpdate)this);
                dt = DateTime.Now;
                Console.WriteLine(dt.ToString() + ": Fetched EPG data");

                // Wait one day before fetching new EPG data
                Thread.Sleep(aDay);
            }
        }

        private void PruneEPGCache(DirectoryInfo dirInfo)
        {
            // Delete all files that contain data earlier than today
            string strToday = String.Format("{0:yyyyMMdd}", DateTime.Today);
            foreach (string epgdatafile in Directory.GetFiles(dirInfo.FullName, "epgdata.*"))
            {
                try
                {
                    int idx = epgdatafile.IndexOf("epgdata.") + "epgdata.".Length;
                    string strData = epgdatafile.Substring(idx, 8);
                    // I use the fact that the date is contained in the filename
                    if (Int32.Parse(strData) < Int32.Parse(strToday))
                    {
                        // Before deleting the program metadata file, delete all programs this 
                        // file refers to
                        DeleteProgramFiles(epgdatafile);
                        File.Delete(epgdatafile);
                    }
                }
                catch (Exception)
                {
                    eventLogOBNEPGGrabber.WriteEntry("Error purging EPG cache", EventLogEntryType.Error);
                }
            }
        }

        private void DeleteProgramFiles(string epgDataFile)
        {
            string text = System.IO.File.ReadAllText(epgDataFile);

            JsonTextParser jsonParser = new JsonTextParser();
            JsonObject jsonObjects = jsonParser.Parse(text);

            foreach (JsonObject jsonObj in jsonObjects as JsonObjectCollection)
            {
                for (int i = 0; i < ((JsonArrayCollection)jsonObj).Count; i++)
                {
                    var o = ((JsonArrayCollection)jsonObj)[i];
                    foreach (JsonObject obj in o as JsonObjectCollection)
                    {
                        if (obj.Name == "id")
                        {
                            string strID = (string)obj.GetValue();
                            string strProgramFile = strID + ".json";
                            if (File.Exists(m_diEPG.FullName + "\\" + strID + ".json"))
                            {
                                File.Delete(m_diEPG.FullName + "\\" + strID + ".json");
                            }
                        }
                    }
                }
            }
        }

        private DirectoryInfo CreateTempFolder(string strFolderName, IStatusUpdate IUpdate)
        {
            string strTemp = System.IO.Path.GetTempPath();

            try
            {
                if (!Directory.Exists(strTemp + "OBNEPGGrabber"))
                {
                    Directory.CreateDirectory(strTemp + "OBNEPGGrabber");
                }
                if (!Directory.Exists(strTemp + "\\OBNEPGGrabber\\" + strFolderName))
                {
                    Directory.CreateDirectory(strTemp + "\\OBNEPGGrabber\\" + strFolderName);
                }
                UpdateStatus("Created " + strTemp + "\\OBNEPGGrabber\\" + strFolderName, false);
            }
            catch (Exception)
            {
                UpdateStatus("Error creating temporary folders", true);
            }

            return new DirectoryInfo(strTemp + "\\OBNEPGGrabber\\" + strFolderName);
        }

        protected override void OnStart(string[] args)
        {
            eventLogOBNEPGGrabber.WriteEntry("Starting OBNEPGGrabber service");
            m_thrdFetcher = new Thread(new ThreadStart(FetchData));
            m_thrdFetcher.Start();
        }

        protected override void OnStop()
        {
            eventLogOBNEPGGrabber.WriteEntry("Stopping OBNEPGGrabber service");
            m_thrdFetcher.Abort();
            m_thrdFetcher.Join();
        }
    }
}
